package jp.dodododo.aop.interceptors;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import javax.interceptor.AroundInvoke;
import javax.interceptor.InvocationContext;

import org.aopalliance.intercept.MethodInvocation;

public class JeeSpecInterceptorWrapper extends AbstractMethodInterceptor {

	private Object interceptor;

	public JeeSpecInterceptorWrapper(Object interceptor) {
		this.interceptor = interceptor;
	}

	public Object invoke(MethodInvocation invocation) throws Throwable {
		InvocationContext context = createInvocationContext(invocation);
		Method aroundInvokeMethod = context.getMethod();
		return aroundInvokeMethod.invoke(interceptor, context);
	}

	private InvocationContext createInvocationContext(MethodInvocation invocation) throws NoSuchMethodException {
		Method method = getAroundInvokeMethod(interceptor);
		return new InvocationContextImpl(method, invocation);
	}

	private static Method getAroundInvokeMethod(Object interceptor) throws NoSuchMethodException {
		Method[] methods = interceptor.getClass().getMethods();
		for (int i = 0; i < methods.length; i++) {
			Method method = methods[i];
			AroundInvoke annotation = method.getAnnotation(AroundInvoke.class);
			if (annotation != null) {
				return method;
			}
		}
		throw new NoSuchMethodException("@AroundInvoke was not found.");
	}

	public static class InvocationContextImpl implements InvocationContext {
		private Method mthod;

		private MethodInvocation invocation;

		private Object[] parameters;

		public InvocationContextImpl(Method method, MethodInvocation invocation) {
			this.mthod = method;
			this.invocation = invocation;
			this.parameters = invocation.getArguments();
		}

		public Map<String, Object> getContextData() {
			return new HashMap<String, Object>();
		}

		public Method getMethod() {
			return mthod;
		}

		public Object[] getParameters() {
			return parameters;
		}

		public Object getTarget() {
			return invocation.getThis();
		}

		public Object proceed() throws Exception {
			try {
				return invocation.proceed();
			} catch (Exception e) {
				throw e;
			} catch (Throwable t) {
				throw new Exception(t);
			}
		}

		public void setParameters(Object[] parameters) {
			this.parameters = parameters;
		}
	}
}
